﻿using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using XmlUtilities;

namespace SAGhoul.Tartareosity
{
	public class Portal
	{
		public Vector3 Min; //传送门位置最小点
		public Vector3 Max; //传送门位置最大点
	}

	public class LoadScreenLoader : ModLoader
	{
		SubsystemTartareosity SubsystemTartareosity;
		public override void __ModInitialize()
		{
			ModsManager.RegisterHook("CalculateLighting", this);
			ModsManager.RegisterHook("BeforeGameLoading", this);
			ModsManager.RegisterHook("OnProjectLoaded", this);
			ModsManager.RegisterHook("SkyDrawExtra", this);
		}
		public override void CalculateLighting(ref float brightness)
		{
			if (SubsystemTartareosity?.WorldType == WorldType.Tartareosity)
			{
				brightness = 5f;
			}
		}
		/// <summary>
		/// 存档开始加载前执行
		/// </summary>
		/// <param name="playScreen">世界选择界面</param>
		/// <param name="item">类型为WorldInfo，存储即将进入的存档信息</param>
		/// <returns>返回类型应当保证为WorldInfo，返回null可以阻止玩家进入存档</returns>
		public override object BeforeGameLoading(PlayScreen playScreen, object item)
		{
			string path = ((WorldInfo)item).DirectoryName;
			string wpath = string.Empty;
			XElement xElement = null;
			//XElement subXElement = null;
			try
			{
				using (Stream stream = Storage.OpenFile(Storage.CombinePaths(path, "Project.xml"), OpenFileMode.Read))
				{
					xElement = XmlUtils.LoadXmlFromStream(stream, null, throwOnError: true);
				}
				foreach (XElement e in xElement.Element("Subsystems").Elements())
				{
					if (XmlUtils.GetAttributeValue<string>(e, "Name") == "Tartareosity")
					{
						wpath = XmlUtils.GetAttributeValue<string>(e.Element("Value"), "Value");
					}
				}
				if (wpath != string.Empty)
				{
					if (!SubsystemWorld.IsChildWorld(wpath))
					{
						if (path != wpath)
						{
							wpath = path;
						}
					}
					else
					{
						if (path != Storage.GetDirectoryName(wpath))
						{
							wpath = Storage.CombinePaths(path, Storage.GetFileName(wpath));
						}
					}
					//using Stream stream = Storage.OpenFile(Storage.CombinePaths(wpath, "Project.xml"), OpenFileMode.Read);
					//subXElement = XmlUtils.LoadXmlFromStream(stream, null, throwOnError: true);
					////存档设置与主世界同步
					//SubsystemWorld.SynchronizeGameInfo(xElement, subXElement, path, wpath, false);
				}
				else
				{
					wpath = path;
				}
				//进入上一次退出的存档
				GameLoadingScreen gameLoadingScreen = ScreensManager.FindScreen<GameLoadingScreen>("GameLoading");
				gameLoadingScreen.m_worldInfo = WorldsManager.GetWorldInfo(wpath);
				return gameLoadingScreen.m_worldInfo;
			}
			catch (Exception e)
			{
				Log.Error("Failed load subworld: " + e.Message);
				return item;
			}
		}
		public override void OnProjectLoaded(Project project)
		{
			SubsystemTartareosity = project.FindSubsystem<SubsystemTartareosity>(true);
			TartareosityTerrainGenerator.CreateRockBrush();
			LightingManager.CalculateLightingTables();
			if(SubsystemTartareosity.WorldType == WorldType.Tartareosity)
				SubsystemSky.DrawGalaxyEnabled = false;//子世界禁用星空和日月贴图绘制
			else
				SubsystemSky.DrawGalaxyEnabled = true;
		}

		public override void SkyDrawExtra(SubsystemSky subsystemSky, Camera camera)
		{
			if(subsystemSky.Project.FindSubsystem<SubsystemTartareosity>()?.WorldType != WorldType.Tartareosity)
				return;//由于子世界星空与日月贴图绘制一起被强制禁用，所以这里为子世界时重新绘制星空（日月仍禁用）
			float precipitationIntensity = subsystemSky.m_subsystemWeather.PrecipitationIntensity;
			float timeOfDay = subsystemSky.m_subsystemTimeOfDay.TimeOfDay;
			if (subsystemSky.m_starsVertexBuffer == null
				|| subsystemSky.m_starsIndexBuffer == null)
			{
				Utilities.Dispose(ref subsystemSky.m_starsVertexBuffer);
				Utilities.Dispose(ref subsystemSky.m_starsIndexBuffer);
				subsystemSky.m_starsVertexBuffer = new VertexBuffer(subsystemSky.m_starsVertexDeclaration, SubsystemSky.m_starsCount * 4);
				subsystemSky.m_starsIndexBuffer = new IndexBuffer(IndexFormat.SixteenBits, SubsystemSky.m_starsCount * 6);
				subsystemSky.FillStarsBuffers();
			}
			Display.DepthStencilState = DepthStencilState.DepthRead;
			Display.RasterizerState = RasterizerState.CullNoneScissor;
			float num = MathUtils.Sqr((1f - subsystemSky.CalculateLightIntensity(timeOfDay)) * (1f - precipitationIntensity));
			num *= 1f - subsystemSky.CalculateSkyFog(camera.ViewPosition);
			if (num > 0.01f)
			{
				Display.BlendState = BlendState.Additive;
				SubsystemSky.m_shaderTextured.Transforms.World[0] = Matrix.CreateRotationZ(-2f * timeOfDay * (float)Math.PI)
					* Matrix.CreateRotationX(subsystemSky.CalculateSeasonAngle())
					* Matrix.CreateTranslation(camera.ViewPosition)
					* camera.ViewProjectionMatrix;
				SubsystemSky.m_shaderTextured.Color = new Vector4(1f, 1f, 1f, num);
				SubsystemSky.m_shaderTextured.Texture = ContentManager.Get<Texture2D>("Textures/Star");
				SubsystemSky.m_shaderTextured.SamplerState = SamplerState.LinearClamp;
				Display.DrawIndexed(
					PrimitiveType.TriangleList,
					SubsystemSky.m_shaderTextured,
					subsystemSky.m_starsVertexBuffer,
					subsystemSky.m_starsIndexBuffer,
					0,
					subsystemSky.m_starsIndexBuffer.IndicesCount
				);
			}
		}
	}

	public enum WorldType
	{//Default对应主世界，无特别需要不修改
		Default, Tartareosity
	}
	public enum WorldTransferType
	{
		ToLastWorld,
		ChildToMajor,
		MajorToChild,
		ChildToChild
	}

	public class TartareosityTerrainGenerator(SubsystemTerrain subsystemTerrain) : TerrainContentsGenerator24(subsystemTerrain), ITerrainContentsGenerator
	{
		static readonly List<TerrainBrush> m_rockPocketBrushes;

		public SubsystemTerrain SubsystemTerrain
		{
			get; set;
		} = subsystemTerrain;
		static TartareosityTerrainGenerator()
		{
			m_rockPocketBrushes = [];
			CreateRockBrush();
		}
		public static void CreateRockBrush()
		{//由于动态id，每次进存档都需要重新创建笔刷
			m_rockPocketBrushes.Clear();
			Game.Random random = new(17);
			for (int num40 = 0; num40 < 16; num40++)
			{
				TerrainBrush terrainBrush10 = new();
				int num41 = random.Int(16, 32);
				for (int num42 = 0; num42 < num41; num42++)
				{
					Vector3 vector10 = 0.5f * Vector3.Normalize(new Vector3(random.Float(-1f, 1f), random.Float(-0.75f, 0.75f), random.Float(-1f, 1f)));
					int num43 = random.Int(6, 12);
					Vector3 zero10 = Vector3.Zero;
					for (int num44 = 0; num44 < num43; num44++)
					{
						terrainBrush10.AddBox((int)MathF.Floor(zero10.X), (int)MathF.Floor(zero10.Y), (int)MathF.Floor(zero10.Z), 1, 1, 1, typeof(TartareosityRockBlock).BlockIndex());
						zero10 += vector10;
					}
				}
				terrainBrush10.Compile();
				m_rockPocketBrushes.Add(terrainBrush10);
			}
		}
		public override float CalculateOceanShoreDistance(float x, float z) => 9999f;
		public override float CalculateMountainRangeFactor(float x, float z)
		{
			//return SimplexNoise.OctavedNoise(x + m_mountainsOffset.X, z + m_mountainsOffset.Y, TGMountainRangeFreq / TGBiomeScaling, 3, 1.91f, 0.75f, ridged: false);
			float frequency = TGMountainRangeFreq;// / TGBiomeScaling; // 频率
			float amplitude = 1f; // 振幅，为1才能生成小山包
			return amplitude * SimplexNoise.OctavedNoise(
				x + m_mountainsOffset.X,
				z + m_mountainsOffset.Y,
				frequency,
				2, // octave，调大了之后一望无垠
				3f, // lacunarity，似乎越小则地形越密集
				0.5f, // persistence，没看出来作用是什么，似乎越小则差异越小？
				ridged: false //山脉
			);
		}
		public override int CalculateTemperature(float x, float z) => Math.Clamp(12 + (int)m_worldSettings.TemperatureOffset, 0, 15);
		public override int CalculateHumidity(float x, float z) => Math.Clamp(12 + (int)m_worldSettings.HumidityOffset, 0, 15);
		public override float CalculateHeight(float x, float z)
		{
			float num = TGOceanSlope
				+ TGOceanSlopeVariation
				* MathUtils.PowSign(2f * SimplexNoise.OctavedNoise(x + m_mountainsOffset.X, z + m_mountainsOffset.Y, 0.01f, 1, 2f, 0.5f) - 1f, 0.5f);
			float num2 = CalculateOceanShoreDistance(x, z);
			float num3 = MathUtils.Saturate(2f - 0.05f * MathF.Abs(num2));
			float num4 = MathUtils.Saturate(MathF.Sin(TGIslandsFrequency * num2));
			float num5 = MathUtils.Saturate(MathUtils.Saturate((0f - num) * num2) - 0.85f * num4);
			float num6 = MathUtils.Saturate(MathUtils.Saturate(0.05f * (0f - num2 - 10f)) - num4);
			float v = CalculateMountainRangeFactor(x, z);
			float f = (1f - num3) * SimplexNoise.OctavedNoise(x, z, 0.001f / TGBiomeScaling * 0f, 2, 2f, 0.5f);
			float f2 = (1f - num3) * SimplexNoise.OctavedNoise(x, z, 0.0017f / TGBiomeScaling * 0f, 2, 4f, 0.7f);
			float num7 = (1f - num6) * (1f - num3) * Squish(v, 1f - TGHillsPercentage, 1f - TGMountainsPercentage);
			float num8 = (1f - num6) * Squish(v, 1f - TGMountainsPercentage, 1f);
			float num9 = 1f * SimplexNoise.OctavedNoise(x, z, TGHillsFrequency, TGHillsOctaves, 1.93f, TGHillsPersistence);
			float amplitudeStep = MathUtils.Lerp(0.75f * TGMountainsDetailPersistence, 1.33f * TGMountainsDetailPersistence, f);
			float num10 = 1.5f * SimplexNoise.OctavedNoise(x, z, TGMountainsDetailFreq, TGMountainsDetailOctaves, 1.98f, amplitudeStep) - 0.5f;
			float num11 = MathUtils.Lerp(80f, 35f, MathUtils.Saturate(1f * num8 + 0.5f * num7 + MathUtils.Saturate(1f - num2 / 30f)));
			float x2 = MathUtils.Lerp(-2f, -4f, MathUtils.Saturate(num8 + 0.5f * num7));
			float num12 = MathUtils.Saturate(
				1.5f - num11 * MathF.Abs(2f * SimplexNoise.OctavedNoise(x + m_riversOffset.X, z + m_riversOffset.Y, 0.001f, 4, 2f, 0.5f) - 1f)
			);
			float num13 = -50f * num5 + TGHeightBias;
			float num14 = MathUtils.Lerp(0f, 8f, f);
			float num15 = MathUtils.Lerp(0f, -6f, f2);
			float num16 = TGHillsStrength * num7 * num9;
			float num17 = TGMountainsStrength * num8 * num10;
			float f3 = 0f;//禁用河流生成 TGRiversStrength * num12;
			float num18 = num13 + num14 + num15 + num17 + num16;
			float num19 = MathUtils.Min(MathUtils.Lerp(num18, x2, f3), num18);
			return Math.Clamp(64f + num19, 10f, 251f);
		}

		public new void GenerateChunkContentsPass1(TerrainChunk chunk)
		{
			GenerateSurfaceParameters(chunk, 0, 0, 16, 8);//生成前半部分温湿度参数
			GenerateTerrain(chunk, 0, 0, 16, 8);//生成前半部分地形
		}

		public new void GenerateChunkContentsPass2(TerrainChunk chunk)
		{
			GenerateSurfaceParameters(chunk, 0, 8, 16, 16);//生成后半部分温湿度参数
			GenerateTerrain(chunk, 0, 8, 16, 16);//生成后半部分地形
		}

		public new void GenerateChunkContentsPass3(TerrainChunk chunk)
		{
			GenerateRockPockets(chunk);//生成岩石
			PropagateFluidsDownwards(chunk);//向下填充流体
		}

		public new void GenerateChunkContentsPass4(TerrainChunk chunk)
		{
			GenerateBasaltColumns(chunk);//生成玄武岩柱
			GenerateBedrockAndAir(chunk);//生成基岩和空气
			UpdateFluidIsTop(chunk);//更新流体顶层状态
		}
		public override void GenerateSurfaceParameters(TerrainChunk chunk, int x1, int z1, int x2, int z2)
		{
			for (int i = x1; i < x2; i++)
			{
				for (int j = z1; j < z2; j++)
				{
					int x = i + chunk.Origin.X;
					int z = j + chunk.Origin.Y;
					chunk.SetTemperatureFast(i, j, CalculateTemperature(x, z));
					chunk.SetHumidityFast(i, j, CalculateHumidity(x, z));
				}
			}
		}

		public override void GenerateTerrain(TerrainChunk chunk, int x1, int z1, int x2, int z2)
		{
			int num = x2 - x1;
			int num2 = z2 - z1;
			_ = m_subsystemTerrain.Terrain;
			int num3 = chunk.Origin.X + x1;
			int num4 = chunk.Origin.Y + z1;
			Grid2d grid2d = new(num, num2);
			Grid2d grid2d2 = new(num, num2);
			for (int i = 0; i < num2; i++)
			{
				for (int j = 0; j < num; j++)
				{
					grid2d.Set(j, i, CalculateOceanShoreDistance(j + num3, i + num4));
					grid2d2.Set(j, i, CalculateMountainRangeFactor(j + num3, i + num4));
				}
			}
			Grid3d grid3d = new(num / 4 + 1, 33, num2 / 4 + 1);
			for (int k = 0; k < grid3d.SizeX; k++)
			{
				for (int l = 0; l < grid3d.SizeZ; l++)
				{
					int num5 = k * 4 + num3;
					int num6 = l * 4 + num4;
					float num7 = CalculateHeight(num5, num6);
					float v = CalculateMountainRangeFactor(num5, num6);
					float num8 = MathUtils.Lerp(TGMinTurbulence, 1f, Squish(v, TGTurbulenceZero, 1f));
					for (int m = 0; m < grid3d.SizeY; m++)
					{
						int num9 = m * 8;
						float num10 = TGTurbulenceStrength * num8 * MathUtils.Saturate(num7 - num9) * (2f * SimplexNoise.OctavedNoise(num5, num9, num6, TGTurbulenceFreq, TGTurbulenceOctaves, 4f, TGTurbulencePersistence) - 1f);
						float num11 = num9 + num10;
						float num12 = num7 - num11;
						num12 += MathUtils.Max(4f * (TGDensityBias - num9), 0f);
						grid3d.Set(k, m, l, num12);
					}
				}
			}
			int oceanLevel = OceanLevel;
			for (int n = 0; n < grid3d.SizeX - 1; n++)
			{
				for (int num13 = 0; num13 < grid3d.SizeZ - 1; num13++)
				{
					for (int num14 = 0; num14 < grid3d.SizeY - 1; num14++)
					{
						grid3d.Get8(n, num14, num13, out float v2, out float v3, out float v4, out float v5, out float v6, out float v7, out float v8, out float v9);
						float num15 = (v3 - v2) / 4f;
						float num16 = (v5 - v4) / 4f;
						float num17 = (v7 - v6) / 4f;
						float num18 = (v9 - v8) / 4f;
						float num19 = v2;
						float num20 = v4;
						float num21 = v6;
						float num22 = v8;
						for (int num23 = 0; num23 < 4; num23++)
						{
							float num24 = (num21 - num19) / 4f;
							float num25 = (num22 - num20) / 4f;
							float num26 = num19;
							float num27 = num20;
							for (int num28 = 0; num28 < 4; num28++)
							{
								float num29 = (num27 - num26) / 8f;
								float num30 = num26;
								int num31 = num23 + n * 4;
								int num32 = num28 + num13 * 4;
								int x3 = x1 + num31;
								int z3 = z1 + num32;
								int num36 = TerrainChunk.CalculateCellIndex(x3, 0, z3);
								for (int num37 = 0; num37 < 8; num37++)
								{
									int num38 = num37 + num14 * 8;
									int value = 0;
									if (num30 < 0f)
									{//低于海平面生成岩浆
										if (num38 <= oceanLevel)
											value = typeof(MagmaBlock).BlockIndex();
									}
									else
									{//生成土层
										value = typeof(TartareosityDirtBlock).BlockIndex();
									}
									chunk.SetCellValueFast(num36 + num38, value);
									num30 += num29;
								}
								num26 += num24;
								num27 += num25;
							}
							num19 += num15;
							num20 += num16;
							num21 += num17;
							num22 += num18;
						}
					}
				}
			}
		}
		public void GenerateRockPockets(TerrainChunk chunk)
		{
			if (!TGCavesAndPockets)
			{
				return;
			}
			for (int i = -1; i <= 1; i++)
			{
				for (int j = -1; j <= 1; j++)
				{
					int num = i + chunk.Coords.X;
					int num2 = j + chunk.Coords.Y;
					Game.Random random = new(m_seed + num + 71 * num2);
					int num3 = random.Int(0, 10);
					for (int k = 0; k < num3; k++)
						random.Int(0, 1);
					for (int n = 0; n < 2; n++)
					{
						int x3 = num * 16 + random.Int(0, 15);
						int y3 = random.Int(20, 120);
						int z3 = num2 * 16 + random.Int(0, 15);
						m_rockPocketBrushes[random.Int(0, m_rockPocketBrushes.Count - 1)].PaintFastSelective(chunk, x3, y3, z3, typeof(TartareosityDirtBlock).BlockIndex());
					}
				}
			}
		}

		public void GenerateBasaltColumns(TerrainChunk chunk)
		{
			if (!TGExtras)
			{
				return;
			}
			int x = chunk.Coords.X;
			int y = chunk.Coords.Y;
			Game.Random random = new(m_seed + x + 1991 * y);
			if (!random.Bool(0.015f))
			{
				return;
			}
			int num2 = random.Int(3, 12);
			int num3 = random.Int(3, 12);
			for (int j = 0; j < 8; j++)
			{
				int num4 = num2 + random.Int(-2, 2);
				int num5 = num3 + random.Int(-2, 2);
				for (int num6 = 251; num6 >= 0; num6--)
				{
					if (Terrain.ExtractContents(chunk.GetCellValueFast(num4, num6, num5)) == typeof(TartareosityDirtBlock).BlockIndex())
					{
						int height = random.Int(1, 6);
						for (int k = num6 + 1;
							k <= num6 + height
							&& chunk.GetCellContentsFast(num4 + 1, k, num5) == 0
							&& chunk.GetCellContentsFast(num4 - 1, k, num5) == 0
							&& chunk.GetCellContentsFast(num4, k, num5 + 1) == 0
							&& chunk.GetCellContentsFast(num4, k, num5 - 1) == 0;
							k++)
						{
							chunk.SetCellValueFast(num4, k, num5, typeof(TartareosityBasaltBlock).BlockIndex());
						}
						break;
					}
				}
			}
		}

	}

}

